define(
	["webgl", "bitmapFont", "transform", "assetSyst", "cam", "objectPool"],
	function(webgl, bmf, tf, assetSyst, cam, objectPool) {
		return {
			create: function(fntPath, ccam, onReady) {
		
				function _compCreate() {
					var _txt = null;
					var _self= null;
					
					/**
					 * Generate polygon for glyph, and put the resulting polygon in posArr, texArr, and indicesArr
					 *
					 * glyph: the glyph
					 * idx: the index of the glyph in a character sequence
					 * x: the x position of this glyph
					 * y: the y position of this glyph
					 * posArr: array containing position information (vec2)
					 * texArr: array containing texture information (vec2)
					 * indicesArr: 
					 */
					function _genGlyphPoly(glyph, idx, x, y, posArr, texArr, indicesArr) {
						//pos
						var posIdx = idx * 4 * 2;
						
						//pos - TL
						posArr[posIdx + 2 * 0 + 0] = x + glyph.xoff;
						posArr[posIdx + 2 * 0 + 1] = y - glyph.yoff;
						
						//pos - BL
						posArr[posIdx + 2 * 1 + 0] = x + glyph.xoff;
						posArr[posIdx + 2 * 1 + 1] = y - glyph.h - glyph.yoff;
						
						//pos - TR
						posArr[posIdx + 2 * 2 + 0] = x + glyph.w + glyph.xoff;
						posArr[posIdx + 2 * 2 + 1] = y - glyph.yoff;
						
						//pos - BR
						posArr[posIdx + 2 * 3 + 0] = x + glyph.w + glyph.xoff;
						posArr[posIdx + 2 * 3 + 1] = y - glyph.h - glyph.yoff;
						
						
						//tex
						var texIdx = idx * 4 * 2;
						
						//tex - TL
						texArr[texIdx + 2 * 0 + 0] = glyph.u;
						texArr[texIdx + 2 * 0 + 1] = glyph.v;
						
						//tex - BL
						texArr[texIdx + 2 * 1 + 0] = glyph.u;
						texArr[texIdx + 2 * 1 + 1] = glyph.v2;
						
						//tex - TR
						texArr[texIdx + 2 * 2 + 0] = glyph.u2;
						texArr[texIdx + 2 * 2 + 1] = glyph.v;
						
						//tex - BR
						texArr[texIdx + 2 * 3 + 0] = glyph.u2;
						texArr[texIdx + 2 * 3 + 1] = glyph.v2;
						
						
						//indices
						var indIdx 	= idx * 6;
						posIdx		= idx * 4;
						indicesArr[indIdx + 0] = posIdx + 0;
						indicesArr[indIdx + 1] = posIdx + 1;
						indicesArr[indIdx + 2] = posIdx + 2;
						indicesArr[indIdx + 3] = posIdx + 3;
						indicesArr[indIdx + 4] = posIdx + 2;
						indicesArr[indIdx + 5] = posIdx + 1;
					}
					
					function _genVBO(buffer, pos, tex, indices) {
						buffer.pos 		= webgl.createStaticBuf(webgl.gl.ARRAY_BUFFER, Float32Array, pos, 2);
						buffer.texCoord	= webgl.createStaticBuf(webgl.gl.ARRAY_BUFFER, Float32Array, tex, 2);
						buffer.indices	= webgl.createStaticBuf(webgl.gl.ELEMENT_ARRAY_BUFFER, Uint16Array, indices, 1);
					}
					
					function _destroyBuffer() {
						gl.deleteBuffer(_self.buffer.pos);
						gl.deleteBuffer(_self.buffer.texCoord);
						gl.deleteBuffer(_self.buffer.indices);
						
						_self.buffer.pos = _self.buffer.texCoord = _self.buffer.indices = null;
					}
					
					function _generateBuffer() {
						var x 		= 0;
						var y 		= 0;
						var pos 	= [];
						var tex 	= [];
						var indices = [];
						for(var i = 0; i < _txt.length; ++i) {
							var glyph = _bmf.glyphs[_txt.charCodeAt(i)];
							_genGlyphPoly(glyph, i, x, y, pos, tex, indices);
							x += glyph.xadv;
						}
						_genVBO(_self.buffer, pos, tex, indices);
					}
					
					function _createComp() {
						var obj = {};
						obj.setText = function(newText) {
							//If buffer already exists, destory it first
							if(this.buffer.pos) 
								_destroyBuffer();
								
							//Set the text and create it if necessary
							if(_txt = newText)
								_generateBuffer();
						};
						obj.getText = function() {
							return _txt;
						};
						obj.transform	= tf.create();
						obj.buffer = {
							pos: null,
							texCoord: null,
							indices: null,
						};
						return obj;
					}
					
					return _self = _createComp();
				}
				
				function _compDestroy(obj) {
					obj.setText(); //destroy the buffer
				}
		
				function _doneProcessing() {
					if(--_toProcess <= 0) {
						//Trigger callback
						if(onReady)
							onReady(this);
					}
				}
				
				function _handleBmfTexture() {
					_tex = gl.createTexture();
					gl.bindTexture(gl.TEXTURE_2D, _tex);
					//gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
					gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, _bmf.images[0].img);
					gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
					gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
					gl.bindTexture(gl.TEXTURE_2D, null);
					
					_doneProcessing();
				}
		
				function _loadAssets() {
					++_toProcess;
					
					//load shader
					++_toProcess;
					_shader	= assetSyst.get("vPolyText|fPolyText|shader.sp", _doneProcessing);
					
					//load bitmapFont
					++_toProcess;
					_bmf	= assetSyst.get(fntPath, _handleBmfTexture);
					
					_doneProcessing();
				}
				
				function _applyShader() {
					gl.useProgram(_shader.program);
					gl.uniformMatrix4fv(_shader.getUniform("uVMat"), false, _cam.getView());
					gl.uniformMatrix4fv(_shader.getUniform("uPMat"), false, _cam.getPerspective());	
					
					gl.activeTexture(gl.TEXTURE0);
					gl.bindTexture(gl.TEXTURE_2D, _tex);
					gl.uniform1i(_shader.getUniform("uSampler"), 0);
				}
				
				function _applyModel(comp) {
					webgl.pointAttrib(_shader.getAttribute("aPos"), comp.buffer.pos);
					webgl.pointAttrib(_shader.getAttribute("aTexCoord"), comp.buffer.texCoord);
				}
				
				function _applyTransform(comp) {
					gl.uniformMatrix4fv(_shader.getUniform("uMMat"), false, comp.transform.getMatrix());
				}
				
				function _drawCall(comp) {
					gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, comp.buffer.indices);
					gl.drawElements(gl.TRIANGLES, comp.buffer.indices.numItems, gl.UNSIGNED_SHORT, 0);
				}
				
				var _pool		= objectPool.create(_compCreate, _compDestroy);
				var _bmf		= null;
				var _shader		= null;
				var _tex		= null;
				var _cam		= ccam;
				var _toProcess 	= 0;
				var gl			= webgl.gl;
				
				_loadAssets();
			
				return {
					
					//Generate text
					getComp: function() {
						return _pool.get();
					},
					
					letComp: function(comp) {
						_pool.let(comp);
					},
					
					render: function() {
						if(!_shader.isLoaded())
							return;
							
						_applyShader();
						
						for(var i = 0; i < _pool.objects.length; ++i) {
							if(_pool.deadFlags[i]) 
								continue;
							
							var comp = _pool.objects[i];
							if(!comp.buffer.pos)
								continue;
							
							_applyTransform(comp);
							_applyModel(comp);
							_drawCall(comp);
						}
					}
				};
			}
		}
	}
);